/**
 * @class fish.desktop.widget.PlanCombobox
 * 下拉框控件
 * <pre>
 *   $(element).plancombobox(option);
 * </pre>
 */

/**
 * @event open
 * 显示下拉框选项事件
 *
 * <pre>
 * $(element).on('foldercombobox:open', function(e) {
 *   //do something
 * })
 * </pre>
 */
/**
 * @event close
 * 关闭下拉框选项事件
 *
 * <pre>
 * $(element).on('foldercombobox:close', function(e) {
 *   //do something
 * })
 * </pre>
 */
/**
 * @event change
 * 改变下拉框值
 *
 * <pre>
 * $(element).on('foldercombobox:change', function(e) {
 *   //do something
 * })
 * </pre>
 */
!function () {
    "use strict";

    var template = `<div class="input-group ui-combobox">
                        <div class="btn-group dropup">
                            <button type="button" class="btn btn-default">
                                <span class="folder"></span>
                            </button>
                            <button type="button" class="btn btn-default">根目录
                                <span class="folderfileright"></span>
                                <span class="others"></span>
                            </button>
                        </div>
                    </div>`,
        MENU = '<ul class="dropdown-list"></ul>',
        MENU_ITEM = '<li></li>';

    $.widget("ui.foldercombobox", {
        options: {
            /**
             * @cfg {String} text 选中文本内容
             */
            text: null,
            /**
             * @cfg {Object} value 选中值
             */
            value: null,
            /**
             * @cfg {String} [dataTextField='name'] 数据源显示字段
             */
            dataTextField: "name",
            /**
             * @cfg {String} [dataValueField='value'] 数据源取值字段
             */
            dataValueField: "value",
            /**
             * @cfg {Array} [dataSource='name'] 数据源，如果元素是`select`则去其`options`节点
             */
            dataSource: [],
            /**
             * @cfg {String} [placeholder=''] 提示信息
             */
            placeholder: "",
            /**
             * @cfg {Number} [rowCount=10000] 显示结果展示条数
             */
            rowCount: 10000,
            /**
             * @cfg {Number} [delay=300] 延迟搜索，避免过多无效搜索，单位毫秒ms，默认是300ms
             */
            delay: 300

            /**
             * @cfg {Function} [close=null] 定义关闭下拉框选项事件函数，事件名称combobox:close
             */

            /**
             * @cfg {Function} [change=null] 定义改变下拉框值事件函数，事件名称combobox:change
             */

            /**
             * @cfg {Function} [open=null] 定义显示下拉框选项事件函数，事件名称combobox:open
             */
        },
        _create: function () {
            this.$container = $(template);
            this.element.hide().before(this.$container);
            this.$other = this.$container.find('.other');
            this.$other.setWidth(200);
            this.$menu = $(MENU).insertAfter(this.$input);
            //this._delegateEvent();
        },

        _init: function () {
            this.shown = false;
            this.selected = false;
            if (this.element.is('select')) { //如果是select，解析options
                this.options.dataSource = this._parse(); //解析select原始值
            }

            if (!this.selected) {
                this.value(this.options.value);
            }

            this.transferAttributes();
        },
        _validateHandler: function () {
            var $element = this.element,
                that = this;
            $element.on('change.valid blur.valid', function () {
                if (that._getValidFlag()) {
                    $element.isValid();
                }
            });
            this.$input.on('focus.valid', function () {
                if ($element.hasClass('n-invalid')) {
                    $element.resetElement();
                }
            });
        },

        _setOption: function (key, value) {
            this._super(key, value);

            if (key === 'disabled') {
                this.$input.prop('disabled', value);
                this.$button.attr('disabled', value);
            }
        },

        /**
         * @method text 取文本值或者赋文本值
         * @param  {String} [text] 设置文本值,为空则取文本值
         * @return {String} 文本值,赋值操作则没有返回值
         */
        text: function (text) {
            if (text === undefined) {
                return this.$input.val();
            } else {
                this.$input.val(text);
            }
        },

        /**
         * @method value 取值或者赋值
         * @param  {String} [value] 设置值选中,为空则取控件值
         * @return {String} 控件值,赋值操作则没有返回值
         */
        value: function (value) {
            var options = this.options,
                target;
            if (value === undefined) {
                return this.element.val();
            } else {
                target = _.find(options.dataSource, function (item) {
                    if (item[options.dataValueField] === value) {
                        return true;
                    }
                });

                this._select(target);
            }
        },

        clear: function () {
            this._select(null);
        },

        /**
         * @method destroy
         * 销毁控件
         */
        _destroy: function () {
            this.element.prev().remove();
            this.element.show();
        },

        _parse: function () {
            var that = this,
                source = [],
                item,
                option;

            if (this.element.is('select')) {
                this.element.find('option').each(function () {
                    option = $(this);
                    item = {};
                    if (option.val() === '') {
                        that.options.placeholder = option.text();
                        return;
                    }
                    item[that.options.dataTextField] = option.text();
                    item[that.options.dataValueField] = option.val();
                    source.push(item);
                    if (option.prop('selected')) {
                        that.options.text = item[that.options.dataTextField];
                        that.options.value = item[that.options.dataValueField];
                        that._setSelected(true);
                        that.$input.val(that.options.text);
                    }
                });
            }
            return source;
        },
        transferAttributes: function () {
            this.options.placeholder = this.options.placeholder || this.element.attr('data-placeholder') || this.element.attr('placeholder');
            this.$input.attr('placeholder', this.options.placeholder);
            this.$input.attr('title', this.element.attr('title'));
            this.$input.attr('tabindex', this.element.attr('tabindex'));
            this.element.removeAttr('tabindex');
            if (this.element.attr('disabled') !== undefined)
                this.disable();
        },

        _setDataSource: function (dataSource) {
            this.options.dataSource = dataSource;
        },

        show: function () {
            var pos = $.extend({}, this.$input.position(), {
                height: this.$input[0].offsetHeight
            });

            var offset = this.$input.offset();
            var _height = offset.top - $(document).scrollTop() + pos.height + this.$menu.height();
            var parentTop = _height > $(window).height() ? -this.$menu.outerHeight() : (pos.top + pos.height);

            this.$menu.css({
                top: parentTop,
                left: pos.left
            });
            this.$container.addClass('open');

            this.$menu.on('mousedown', $.proxy(this._scrollSafety, this));
            $(document).on('click.fish.combobox', $.proxy(this.hide, this));

            this.shown = true;
            this._trigger('open');
        },

        hide: function () {
            this.$container.removeClass('open');
            this.$menu.off('mousedown', $.proxy(this._scrollSafety, this));
            $(document).off('click.fish.combobox', $.proxy(this.hide, this));
            this.shown = false;
            this._trigger('close');
        },

        lookup: function (query) {
            var that = this,
                items = $.grep(this.options.dataSource, function (item) {
                    return that.matcher(item, query);
                });

            items = this.sorter(items, query);

            if (!items.length) {
                return this.shown ? this.hide() : this;
            }

            return this.render(items.slice(0, parseInt(this.options.rowCount, 10)), query).show();
        },

        matcher: function (item, query) {
            return ~item[this.options.dataTextField].toLowerCase().indexOf(query.toLowerCase()); //转换成数字0 以及非0
        },

        sorter: function (items, query) {
            var beginswith = [],
                caseSensitive = [],
                caseInsensitive = [],
                that = this;

            _.forEach(items, function (item) {
                if (!item[that.options.dataTextField].toLowerCase().indexOf(query.toLowerCase())) {
                    beginswith.push(item);
                } else if (~item[that.options.dataTextField].indexOf(query)) {
                    caseSensitive.push(item);
                } else {
                    caseInsensitive.push(item);
                }
            });

            return beginswith.concat(caseSensitive, caseInsensitive);
        },

        /*
         * 高亮显示
         * @param item
         */
        highlighter: function (item, query) {
            query = query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
            return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
                return '<strong>' + match + '</strong>';
            });
        },

        render: function (items, query) {
            var that = this;

            items = $(items).map(function (i, item) {
                i = $(MENU_ITEM).data('value', item);
                i.html(that.highlighter(item[that.options.dataTextField], query));
                return i[0];
            });

            items.first().addClass('active');
            this.$menu.html(items);
            return this;
        },

        move: function (e) {
            if (!this.shown) {
                return;
            }

            switch (e.keyCode) {
                case 9: // tab
                case 13: // enter
                case 27: // escape
                    e.preventDefault();
                    break;

                case 38: // up arrow
                    e.preventDefault();
                    this.prev();
                    break;

                case 40: // down arrow
                    e.preventDefault();
                    this.next();
                    break;
            }

            e.stopPropagation();
        },

        next: function (event) {
            var active = this.$menu.find('.active').removeClass('active'),
                next = active.next();

            if (!next.length) {
                next = $(this.$menu.find('li')[0]);
            }

            next.addClass('active');
        },

        prev: function (event) {
            var active = this.$menu.find('.active').removeClass('active'),
                prev = active.prev();

            if (!prev.length) {
                prev = this.$menu.find('li').last();
            }

            prev.addClass('active');
        },

        reset: function () {
            if (this.options.disabled) return;

            if (this.selected) {
                this.clearTarget();
                this.clearElement();
                this._triggerChange();
            } else {
                if (this.shown) {
                    this.hide();
                } else {
                    this.clearElement();
                }
            }
        },

        _scrollSafety: function (e) {
            if (e.target.tagName === 'UL') {
                this.$input.off('blur');
            }
        },
        clearElement: function () {
            this.$input.val('').focus();
        },
        clearTarget: function () {
            this.element.val('');
            this._setSelected(false);
        },

        _setSelected: function (selected) {
            this.selected = selected;
        },

        _delegateEvent: function () {
            this.$input
                .on('blur', $.proxy(this._onBlur, this))
                .on('keypress', $.proxy(this._onKeyPress, this))
                .on('keyup', $.proxy(this._onKeyUp, this))
                .on('keydown', $.proxy(this._onKeyDown, this));

            this.$menu
                .on('click', $.proxy(this._onMenuClick, this))
                .on('mouseenter', 'li', $.proxy(this._onMenuMouseEnter, this))
                .on('mouseleave', 'li', $.proxy(this._onMenuMouseLeave, this));

            this.$button
                .on('click', $.proxy(this._onButtonToggle, this));
        },

        _onBlur: function (e) {
            if (this.mousedover) return;

            // when no selected then clear the input value
            if (!this.selected) {
                this.$input.val('');
            }

            if (this.shown) {
                this.hide();
            }

            //input离开的时候,触发comboboxblur供检验用
            this._trigger('blur');
        },

        _onKeyDown: function (e) {
            this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40, 38, 9, 13, 27]);
            this.move(e);
        },
        _onKeyPress: function (e) {
            if (this.suppressKeyPressRepeat) {
                return;
            }
            this.move(e);
        },
        _onKeyUp: function (e) {
            var base = this;
            switch (e.keyCode) {
                case 40: // down arrow
                case 39: // right arrow
                case 38: // up arrow
                case 37: // left arrow
                case 36: // home
                case 35: // end
                case 16: // shift
                case 17: // ctrl
                case 18: // alt
                    break;

                case 9: // tab
                case 13: // enter
                    if (!this.shown) {
                        return;
                    }
                    this._onMenuSelect();
                    break;

                case 27: // escape
                    if (!this.shown) {
                        return;
                    }
                    this.hide();
                    break;
                case 8: //backspace
                case 46: //delete
                     this.clear();
                     break;
                default:
                    this.clearTarget();
                    clearTimeout(this.searching);
                    this.searching = this._delay(function () {
                        base.lookup(base.$input.val());
                    }, this.options.delay);
            }

            e.stopPropagation();
            e.preventDefault();
        },

        _onMenuClick: function (e) {
            e.stopPropagation();
            this._onMenuSelect();
            this.$input.focus();
        },

        _onMenuMouseEnter: function (e) {
            this.mousedover = true;
            this.$menu.find('.active').removeClass('active');
            $(e.currentTarget).addClass('active');
        },

        _onMenuMouseLeave: function (e) {
            this.mousedover = false;
        },

        _onButtonToggle: function (e) {
            e.stopPropagation();

            if (this.options.disabled) return;

            if (this.shown) {
                this.hide();
            } else {
                this.lookup(this.selected ? '' : this.$input.val());
            }
        },

        _onMenuSelect: function () {
            var item = this.$menu.find('.active').data('value');
            this._select(item);
            this.hide();
        },
        //与_select的区别在于old == item.value;form会优先执行reset,clear,setvalue操作
        _update: function () {
            var options = this.options,
                target,
                value = this.element.val();
            if (value) {
                target = _.find(options.dataSource, function (item) {
                    if (item[options.dataValueField] === value) {
                        return true;
                    }
                });
            }
            if (target) {
                this.$input.val(target[options.dataTextField]);
                this._setSelected(true);
            } else {
                this.$input.val('');
                this._setSelected(false);
            }
        },
        _select: function (item) {
            var options = this.options,
                old = this.element.val();
            if (item && item[options.dataValueField] !== old) {
                this.$input.val(item[options.dataTextField]);
                this.element.val(item[options.dataValueField]);
                this._setSelected(true);
                this._triggerChange();
            } else if (this.selected && !item) {
                this.$input.val('');
                this.element.val('');
                this._setSelected(false);
                this._triggerChange();
            }
        },

        _triggerChange: function () {
            this._trigger('change');
        }
    });
}();